/*
 * Copyright 2013 Agustina Martinez-Garcia

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package uk.ljmu.qudexapps.xmllib.services;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.dom4j.Document;
import org.igfay.jfig.JFigException;

import uk.da.dext.datastructs.DocumentStruct;

import uk.ljmu.fedoraifz.restrequests.FedoraOperationsAPIM;
import uk.ljmu.metadata.schemasutilities.SchemasProcessing;
import uk.ljmu.metadata.utilities.MetadataUtilities;
import uk.ljmu.mulgaraifz.errorhandling.MulgaraException;
import uk.ljmu.qudexapps.xmllib.model.ColumnMap;
//import uk.ljmu.qudexapps.xmllib.model.FoxmlBeans;
import uk.ljmu.qudexapps.xmllib.utilities.Constants;
import uk.ljmu.qudexapps.xmllib.utilities.SpreadsheetUtilities.Column;

/**
 * Class with static methods to manage the generation of Fedora Relationships
 * datastreams
 * 
 * @author agustinamartinez
 * 
 */
public class RelationshipsManagement {
    /**
     * LOG log4j
     */
    private static Logger log = Logger.getLogger(RelationshipsManagement.class);

    /**
     * Holds collection object and the relationship e.g. doc1 isMemberOf
     * Collection1, entry 'collection1, isMemberOf'
     */
    private List<String[]> documentsCollectionContainer;

    /**
     * List of documents struct objects to be used when generating the Qudex XML
     */
    private List<DocumentStruct> documentsList;

    /**
     * Array of used Qudex Collections within the documents. This is used to
     * update the qudex instances of the collection parents
     */
    private List<String> qudexCollectionsPIDs;
    /**
     * Used only in documents to validate whether all the sub-collections belong to the same root collection
     */
    private String documentsRootCollection = "";

    /**
     * @param documentsCollectionContainer
     *            the documentsCollectionContainer to set
     */
    public void setDocumentsCollectionContainer(
	    List<String[]> documentsCollectionContainer) {
	this.documentsCollectionContainer = documentsCollectionContainer;
    }

    /**
     * @return the documentsCollectionContainer
     */
    public List<String[]> getDocumentsCollectionContainer() {
	return documentsCollectionContainer;
    }

    /**
     * @param documentsList
     *            the documentsList to set
     */
    public void setDocumentsList(List<DocumentStruct> documentsList) {
	this.documentsList = documentsList;
    }

    /**
     * @return the documentsList
     */
    public List<DocumentStruct> getDocumentsList() {
	return documentsList;
    }

    /**
     * @param qudexCollectionsPIDs
     *            the qudexCollectionsPIDs to set
     */
    public void setQudexCollectionsPIDs(List<String> qudexCollectionsPIDs) {
	this.qudexCollectionsPIDs = qudexCollectionsPIDs;
    }

    /**
     * @return the qudexCollectionsPIDs
     */
    public List<String> getQudexCollectionsPIDs() {
	return qudexCollectionsPIDs;
    }

    /**
     * @param documentsRootCollection the documentsRootCollection to set
     */
    public void setDocumentsRootCollection(String documentsRootCollection) {
	this.documentsRootCollection = documentsRootCollection;
    }

    /**
     * @return the documentsRootCollection
     */
    public String getDocumentsRootCollection() {
	return documentsRootCollection;
    }

    public RelationshipsManagement() {
	this.documentsList = new ArrayList<DocumentStruct>();
	this.documentsCollectionContainer = new ArrayList<String[]>();
	this.qudexCollectionsPIDs = new ArrayList<String>();
    }

    /**
     * Deletes a documentStruct instance from the array of documents by PID
     * 
     * @param pid
     *            identifier of the document to remove
     * @return true if removed, false otherwise
     */
    public boolean removeDocumentElementFromList(String pid) {
	int index = 0;
	for (DocumentStruct docS : this.documentsList) {
	    if (docS.getFedoraDocumentPid().equals(pid)) {
		this.documentsList.remove(index);
		return true;
	    }
	    index++;
	}
	return false;
    }
    /**
     * 
     * @param collectionPid
     * @param fedObject
     * @return
     */
    public boolean generateCollectionRelationships(String collectionPid, FedoraOperationsAPIM fedObject) {
	String relURI = MetadataUtilities.MDSchemasUrl.get("rel");
	
	for (String[] pair : this.documentsCollectionContainer) {
	    String relNoprefix;
	    if (pair[1].contains(Constants.PID_DELIMITER)) {
		relNoprefix = pair[1].substring(pair[1].indexOf(Constants.PID_DELIMITER) + 1);
	    } else relNoprefix = pair[1];
	    String results = fedObject.addRelationship(collectionPid, 
		    MetadataUtilities.OBJECTID + collectionPid, 
		    relURI + relNoprefix,
		    MetadataUtilities.OBJECTID + pair[0], 
		    "false", 
		    "");
	    if (results == null)
		return false;
	}	
	//Clean array for this collection element
	this.documentsCollectionContainer.clear();
	
	return true;
    }
    /**
     * 
     * @param documentPid
     * @param fedObject
     * @return
     */
    public boolean generateDocumentRelationships(String documentPid, FedoraOperationsAPIM fedObject) {
	String relURI = MetadataUtilities.MDSchemasUrl.get("rel");
	for (String[] pair : this.documentsCollectionContainer) {
	    String relNoprefix;
	    if (pair[1].contains(Constants.PID_DELIMITER)) {
		relNoprefix = pair[1].substring(pair[1].indexOf(Constants.PID_DELIMITER) + 1);
	    } else relNoprefix = pair[1];
	    String results = fedObject.addRelationship(documentPid, 
		    MetadataUtilities.OBJECTID + documentPid, 
		    relURI + relNoprefix,
		    MetadataUtilities.OBJECTID + pair[0], 
		    "false", 
		    "");
	    if (results == null)
		return false;
	}
	//Clean array for this collection element
	this.documentsCollectionContainer.clear();
	
	return true;
    }

    /**
     * Add a new collection PID relationship name to the map of relationships
     * 
     * @param auxMap
     *            map of collection PID - relationship name pairs
     * @param collectionID
     *            collection PID to add to the map
     * @param relationship
     *            relationship name to add to the map
     */
    /*
     * private void addElementToMap(Map<String, String> auxMap, String
     * collectionID, String relationship) { boolean found = false;
     * 
     * for (String collectionId : auxMap.keySet()) { if
     * (collectionId.equalsIgnoreCase(collectionID)) { found = true; } } if
     * (!found) { auxMap.put(collectionID, relationship); } }
     */

    /**
     * Manages the list of Qudex Collection Objects involved in the process
     * 
     * @param idsList
     *            list of collection objects
     * @param identifier
     *            new collection object PID to add
     */
    public void addElementToArray(List<String> idsList, String identifier) {
	boolean found = false;

	for (String collectionId : idsList) {
	    if (collectionId.equalsIgnoreCase(identifier)) {
		found = true;
	    }
	}
	if (!found) {
	    idsList.add(identifier);
	}
    }
    
    public boolean addRelatedCollection(List<String> idsList, String identifier) {
	if (idsList.size() == 0 || idsList.get(0) == "") {
	    idsList.add(identifier);
	    return true;
	}
	for (String collectionId : idsList) {
	    if (collectionId.equalsIgnoreCase(identifier)) {
		return true;
	    }
	}
	
	return false;
    }

    /**
     * Manages the array of Qudex Collections Objects PIDs.
     * 
     * @param checkExistence
     *            true if object existence will be checked, false otherwise
     * @param collectionID
     *            collection object involved in the relationship PID
     * @param relationship
     *            relationship name
     * 
     * @return true if process OK, false otherwise
     */
    /*
     * private boolean addQudexCollectionToParentsArray(boolean checkExistence,
     * String collectionID, String relationship) { String collectionExists; //
     * Check whether collection exists in the repository if (checkExistence) {
     * collectionExists = this.fedoraObject.getObjectXML(collectionID); if
     * (collectionExists != null && collectionExists
     * .contains(Constants.XML_UTF_VERSION_HEADER)) {
     * this.addElementToMap(this.qudexCollectionParentsMap, collectionID,
     * relationship); return true; } else return false; } else {
     * this.addElementToMap(this.qudexCollectionParentsMap, collectionID,
     * relationship); return true; } }
     */

    /**
     * Add document to collection relationship in the RELS-EXT datastream of the
     * associated Fedora object for the Qudex Document being referenced
     * 
     * @param objectPid 
     * 		  the Fedora Pid of the object its relationships are being managed
     * @param relsDocument
     *            xml-string for the rels-ext datastream
     * @return true if success, false otherwise
     */
    public boolean addQudexCollectionMembershipToDatastream(String objectPid, Document relsDocument) {
	boolean resultsOK;
	String auxId = "";
	/*
	 * for (String qudexCollectionPid : this.qudexCollectionParentsMap
	 * .keySet()) { if
	 * (!qudexCollectionPid.startsWith(MetadataUtilities.OBJECTID)) { auxId
	 * = MetadataUtilities.OBJECTID + qudexCollectionPid; } resultsOK =
	 * SchemasProcessing.addRDFProperty(
	 * MetadataUtilities.SCHEMAS_PREFIXES.rel.toString(),
	 * this.qudexCollectionParentsMap.get(qudexCollectionPid), auxId,
	 * MetadataUtilities.propertyTypes.Resource.toString(), relsDocument);
	 * if (!resultsOK) { // TODO Check what to do if a relationship is not
	 * included in // the RDF document
	 * log.error("Property not added to RELS-EXT: " +
	 * this.qudexCollectionParentsMap .get(qudexCollectionPid) + " for PID:"
	 * + this.foxmlBeans.getFoxmlDoc().getDigitalObject() .getPID()); } }
	 */
	for (String[] pair : this.documentsCollectionContainer) {
	    if (!pair[0]
		.startsWith(MetadataUtilities.OBJECTID)) {
		auxId = MetadataUtilities.OBJECTID
		    + pair[0];
	    }
	    resultsOK = SchemasProcessing.addRDFProperty(
		MetadataUtilities.SCHEMAS_PREFIXES.rel.toString(),
		pair[1], auxId,
		MetadataUtilities.propertyTypes.Resource.toString(),
		relsDocument);
	    if (!resultsOK) {
		// TODO Check what to do if a relationhsip is not included in
		// the RDF document
		log.error("Property not added to RELS-EXT: "
		    + pair[1] + " for PID:"
		    + objectPid);
		return false;
	    }
	}
	return true;
    }
    /**
     * 
     * @param collectionPid
     * @param relationship
     * @param fedObject
     * @return
     */
    public boolean setDocumentsParentCollection(String collectionPid,
	    String relationship, FedoraOperationsAPIM fedObject) {
	//Check whether exists and the relationship is the same
	boolean exists = this.checkPropertyExistence(collectionPid, relationship);
	if (!exists) {
	    String collectionExists = fedObject.getObjectXML(collectionPid);
	    if (collectionExists != null
		    && collectionExists
			    .contains(Constants.XML_UTF_VERSION_HEADER)) {
		String[] newPair = new String[2];
		newPair[0] = collectionPid;
		newPair[1] = relationship;
		this.documentsCollectionContainer.add(newPair);
		return true;
	    } else return false;
	} else return true;
	/*if (this.documentsCollectionContainer[0] == null
		|| this.documentsCollectionContainer[0] == "") {
	    String collectionExists = fedObject.getObjectXML(collectionPid);
	    if (collectionExists != null
		    && collectionExists
			    .contains(Constants.XML_UTF_VERSION_HEADER)) {
		this.documentsCollectionContainer[0] = collectionPid;
		this.documentsCollectionContainer[1] = relationship;
		return true;
	    } else
		return false;
	} else {
	    if (!collectionPid.equals(this.documentsCollectionContainer[0]))
		return false;
	    else
		return true;
	}*/
    }
    private boolean checkPropertyExistence(String collectionPid,
	    String relationship) {
	for (String[] pair : this.documentsCollectionContainer) {
	    if (collectionPid.equals(pair[0]) && relationship.equals(pair[1]))
		return true;
	}
	return false;
    }

    /**
     * 
     */
    public void cleanInstance() {
	this.qudexCollectionsPIDs.clear();
	this.documentsList.clear();
	this.documentsCollectionContainer.clear();
	this.documentsRootCollection = "";
    }
    // Static Utilities
    /**
     * @param labelId
     * @param item
     * @param fileType
     * @return
     */
    public static String findLabel(String labelId, ColumnMap item,
	    String fileType) {
	for (Column column : item.getColumnMap().keySet()) {
	    if (column.getColumnName().equalsIgnoreCase(labelId)) {
		if (fileType.equalsIgnoreCase(MetadataUtilities.MIMETYPES_MAP
			.get("excel"))
			|| fileType
				.equalsIgnoreCase(MetadataUtilities.MIMETYPES_MAP
					.get("excel2"))
			|| fileType
				.equalsIgnoreCase(MetadataUtilities.MIMETYPES_MAP
					.get("excel3"))
			|| fileType
				.equalsIgnoreCase(MetadataUtilities.MIMETYPES_MAP
					.get("excel4"))
			|| fileType
				.equalsIgnoreCase(MetadataUtilities.MIMETYPES_MAP
					.get("excel5"))
			|| fileType
				.equalsIgnoreCase(MetadataUtilities.MIMETYPES_MAP
					.get("excel6"))
			|| fileType
				.equalsIgnoreCase(MetadataUtilities.MIMETYPES_MAP
					.get("excel7"))) {
		    HSSFCell cell = (HSSFCell) item.getColumnMap().get(column);
		    return cell.getStringCellValue();
		} else {
		    String value = (String) item.getColumnMap().get(column);
		    return value;
		}
	    }
	}

	return "";
    }
    /**
     * 
     * @param property
     * @return
     */
    public static boolean isRelationshipProperty(String property) {
	return (property.equals(uk.da.dext.utilities.Constants.FEDORA_MEMBER_1)
		|| property
			.equals(uk.da.dext.utilities.Constants.FEDORA_MEMBER_2) || property
		.equals(uk.da.dext.utilities.Constants.FEDORA_IS_PART)) ? true
		: false;
    }
    /**
     * 
     * @param str
     * @return
     */
    public String getRootCollection(String str) {
	FedoraMulgaraService mulgServ = null;
	try {
	    mulgServ = new FedoraMulgaraServiceImpl();
	    mulgServ.setRootCollectionPid("");
	    if (mulgServ.isSubCollection(str)) {
		// Find root collection
		mulgServ.findRootCollection(str);
		if (!mulgServ.getRootCollectionPid().equals(""))
		    return mulgServ.getRootCollectionPid().replace(MetadataUtilities.OBJECTID, "");
		else return null;
	    } else return str;
	} catch (JFigException e) {
	   log.error("Error in getRootCollection", e);
	   return null;
	} catch (MulgaraException e) {
	    log.error("Error in getRootCollection", e);
	    return null;
	} finally {
	    if (mulgServ != null) {
		mulgServ.cleanInstance();
	    }
	}
    }
    
    public boolean checkCollectionRelationshipValidity(boolean isCollections, String property, String value, QudexFedoraObjects quObject) {
	boolean resultsOK = true;

	if (quObject.objectExists(value) && !quObject.isCollectionWithQudexInstance(value)) {
	    resultsOK = this.setDocumentsParentCollection(value, property,
		    quObject.getFedoraObject());
	} else
	    return false;

	String rootCollection = this.getRootCollection(value);
	if (rootCollection == null)
	    return false;
	
	// Only for documents. Validate that the sub-collection or collection is the same or has the same root collection as the one stored
	if (!isCollections) {
	    if (this.getDocumentsRootCollection().equals(""))
		this.documentsRootCollection = rootCollection;
	    if (!rootCollection.equals(this.documentsRootCollection)) {
		quObject
		.addFedoraResultsMessage(
		    "manager",
		    "There are Documents that belong to different root collections, this is not permitted. Check relationship information in the spreadsheet.");
		return false;
	    }
	}
	//Only if working with a sub-collection and the root collection is needed
	if (!rootCollection.equals(value)) {
	    
	    resultsOK = quObject.isCollectionWithQudexInstance(rootCollection);
	    if (resultsOK) {
		quObject
		    .addFedoraResultsMessage(
			    "manager",
			    "The selected Qudex Collection already presents Qudex Documents. Specify a different Collection PID");
		return false;
	    }
	    resultsOK = this.setDocumentsParentCollection(rootCollection, property,
		quObject.getFedoraObject());
	    if (!isCollections) {
		resultsOK = this.addRelatedCollection(this.qudexCollectionsPIDs, rootCollection);
	    }
	    
	    return resultsOK;
	}
	if (!isCollections) {
	    resultsOK = this.addRelatedCollection(this.qudexCollectionsPIDs, value);
	}
	return resultsOK;
    }
}
